package openjdk.reflection;

import javax.swing.*;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Type involving Wildcard generic (WildcardType)
 */

public class GetGenericTypeExample6 {
    private List<?> list;
    private Map<? extends Number, ? super JFormattedTextField> map;

    public static void main(String... args) throws NoSuchFieldException {
        System.out.println("Example: List<?> list;");
        Field field = GetGenericTypeExample6.class.getDeclaredField("list");
        Type genericType = field.getGenericType();
        printType(genericType, 1);

        System.out.println("Example: Map<? extends Number, ? super JFormattedTextField> map");
        Field field2 = GetGenericTypeExample6.class.getDeclaredField("map");
        Type genericType2 = field2.getGenericType();
        printType(genericType2, 1);
    }

    private static void printType(Type type, int level) {
        if (type == null) {
            print(level, "The Type is null");
            return;
        }
        print(level, "Type class=" + type.getClass());
        if (type.getClass() != Class.class) {
            print(level, "Type class implementing interfaces=" +
                    Arrays.toString(type.getClass().getInterfaces()));
        }
        print(level, "Type name= " + type.getTypeName());
        printWildcardType(type, level);
        printParameterizedType(type, level);
    }

    private static void printParameterizedType(Type type, int level) {
        if (type instanceof ParameterizedType) {
            print(level, "Casting to ParameterizedType");
            ParameterizedType pt = (ParameterizedType) type;
            print(level, "Using ParameterizedType#getOwnerType()");
            Type ownerType = pt.getOwnerType();
            printType(ownerType, level + 1);

            print(level, "Using ParameterizedType#getRawType()");
            Type rawType = pt.getRawType();
            printType(rawType, level + 1);

            print(level, "Using ParameterizedType#getActualTypeArguments()");
            for (Type actualType : pt.getActualTypeArguments()) {
                print(level, "ActualTypeArgument loop");
                printType(actualType, level + 1);
            }
        }
    }

    private static void printWildcardType(Type type, int level) {
        if (type instanceof WildcardType) {
            print(level, "Casting to WildcardType");
            WildcardType wildcardType = (WildcardType) type;
            print(level, "Using WildcardType#getLowerBounds()");
            if (wildcardType.getLowerBounds().length > 0) {
                for (Type lowerBoundType : wildcardType.getLowerBounds()) {
                    print(level, "lowerBoundType loop");
                    printType(lowerBoundType, level + 1);
                }
            } else {
                print(level, "No lowerBoundType found");
            }
            if (wildcardType.getUpperBounds().length > 0) {
                print(level, "Using WildcardType#getUpperBounds()");
                for (Type upperBoundType : wildcardType.getUpperBounds()) {
                    print(level, "upperBoundType loop");
                    printType(upperBoundType, level + 1);
                }
            } else {
                print(level, "No upperBoundType found");
            }
        }
    }

    private static void print(int level, String string) {
        System.out.printf("%" + (level * 4 - 3) + "s\u00A6- %s%n", "", string);
    }
}